use crate::utils::date_time;

/*
 * @Description: token 认证
 * @Version: 1.0
 * @Autor: JiaJun Wu
 * @Date: 2021-09-06 18:06:45
 * @LastEditors: JiaJun Wu
 * @LastEditTime: 2021-09-23 17:35:48
 */
use super::model::user;
use super::toml_conf::SETTING;
use super::utils;
use super::utils::serialize::*;
use bson::doc;
use bson::oid::ObjectId;
use jsonwebtoken::{DecodingKey, EncodingKey, Header, TokenData, Validation};
use rocket::http::Status;
use rocket::request::{FromRequest, Outcome};
use rocket::serde::{Deserialize, Serialize};
use rocket::Request;

/// 默认结构 用户token
#[derive(Deserialize, Serialize, Debug)]
#[serde(crate = "rocket::serde")]
pub struct UserToken {
    /// 过期时间
    pub exp: u64,
    pub id: Option<ObjectId>,
    /// 用户名
    pub username: Option<String>,
    /// 密码
    pub password: Option<String>,
    /// 真实姓名
    pub realname: Option<String>,
    /// 出生年
    pub birth_year: Option<i32>,
    /// 出生月
    pub birth_month: Option<i32>,
    /// 出生日
    pub birth_day: Option<i32>,
    /// 性别 0.女 1.男 3.保密
    pub sex: Option<i32>,
    /// 机构id
    pub org_id: Option<ObjectId>,
    /// 机构名称
    pub org_name: Option<String>,
    /// 用户类型 1.管理员 2.普通用户 3.其他
    pub types: Option<i32>,
    /// 描述/备注
    pub desc: Option<String>,
    pub qq: Option<String>,
    /// 手机号码
    pub phone: Option<String>,
    /// 电子邮箱
    pub email: Option<String>,
    /// 用户状态 1.正常，2.禁用，3.注销
    pub enabled: Option<i32>,
    /// 头像id
    pub portrait: Option<ObjectId>,
}

impl UserToken {
    pub fn to_bson(&self) -> bson::ordered::OrderedDocument {
        let mut doc = doc! {};
        if let Some(id) = self.id.as_ref() {
            doc.insert("_id", id.to_hex());
            let create_time = date_time::date_to_str(id.timestamp() as i64);
            doc.insert("createTime", create_time);
        } else {
            doc.insert("_id", bson::Bson::Null);
            doc.insert("createTime", bson::Bson::Null);
        }

        if let Some(username) = self.username.as_ref() {
            doc.insert("username", username);
        } else {
            doc.insert("username", bson::Bson::Null);
        }

        if let Some(realname) = self.realname.as_ref() {
            doc.insert("realname", realname);
        } else {
            doc.insert("realname", bson::Bson::Null);
        }

        if let Some(birth_year) = self.birth_year {
            doc.insert("birthYear", birth_year);
        } else {
            doc.insert("birthYear", bson::Bson::Null);
        }

        if let Some(birth_month) = self.birth_month {
            doc.insert("birthMonth", birth_month);
        } else {
            doc.insert("birthMonth", bson::Bson::Null);
        }

        if let Some(birth_day) = self.birth_day {
            doc.insert("birthDay", birth_day);
        } else {
            doc.insert("birthDay", bson::Bson::Null);
        }

        if let Some(sex) = self.sex {
            doc.insert("sex", sex);
        } else {
            doc.insert("sex", bson::Bson::Null);
        }

        if let Some(org_id) = self.org_id.as_ref() {
            doc.insert("orgId", org_id.to_hex());
        } else {
            doc.insert("orgId", bson::Bson::Null);
        }

        if let Some(org_name) = self.org_name.as_ref() {
            doc.insert("orgName", org_name);
        } else {
            doc.insert("orgName", bson::Bson::Null);
        }

        if let Some(types) = self.types {
            doc.insert("types", types);
        } else {
            doc.insert("types", bson::Bson::Null);
        }

        if let Some(desc) = self.desc.as_ref() {
            doc.insert("desc", desc);
        } else {
            doc.insert("desc", bson::Bson::Null);
        }

        if let Some(qq) = self.qq.as_ref() {
            doc.insert("qq", qq);
        } else {
            doc.insert("qq", bson::Bson::Null);
        }

        if let Some(phone) = self.phone.as_ref() {
            doc.insert("phone", phone);
        } else {
            doc.insert("phone", bson::Bson::Null);
        }

        if let Some(email) = self.email.as_ref() {
            doc.insert("email", email);
        } else {
            doc.insert("email", bson::Bson::Null);
        }

        if let Some(enabled) = self.enabled {
            doc.insert("enabled", enabled);
        } else {
            doc.insert("enabled", bson::Bson::Null);
        }

        if let Some(portrait) = self.portrait.as_ref() {
            doc.insert("portrait", portrait.to_hex());
        } else {
            doc.insert("portrait", bson::Bson::Null);
        }
        doc
    }
}

/// 生成token
pub fn encode(user: UserToken) -> String {
    jsonwebtoken::encode(
        &Header::default(),
        &user,
        &EncodingKey::from_secret(&*SETTING.secret_key.as_bytes()),
    )
    .unwrap()
}

/// 解码
#[allow(dead_code)]
pub fn decode(token: &str) -> jsonwebtoken::errors::Result<TokenData<UserToken>> {
    jsonwebtoken::decode::<UserToken>(
        token,
        &DecodingKey::from_secret(&*SETTING.secret_key.as_bytes()),
        &Validation::default(),
    )
}

/// 设置token
#[inline]
#[allow(dead_code)]
pub fn set_token(user: &user::UserModel) -> String {
    let now = utils::date_time::timestamp() as u64;
    let playload = UserToken {
        exp: now + SETTING.day_one,
        id: user.id.clone(),
        username: user.username.to_owned(),
        password: user.password.to_owned(),
        realname: user.realname.to_owned(),
        birth_year: user.birth_year,
        birth_month: user.birth_month,
        birth_day: user.birth_day,
        sex: user.sex,
        org_id: user.org_id.to_owned(),
        org_name: user.org_name.to_owned(),
        types: user.types,
        desc: user.desc.to_owned(),
        qq: user.qq.to_owned(),
        phone: user.phone.to_owned(),
        email: user.email.to_owned(),
        enabled: user.enabled,
        portrait: user.portrait.to_owned(),
    };
    encode(playload)
}

/// 校验token
#[inline]
#[allow(dead_code)]
pub fn verify_token(token: &str) -> Option<UserToken> {
    let user = decode(token);
    match user {
        Ok(v) => Some(v.claims),
        Err(_) => None,
    }
}

/// 创建FromRequest的实现，以便可以在每个api端点上提供Conn
#[rocket::async_trait]
impl<'r> FromRequest<'r> for UserToken {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, ()> {
        let cookies = request.cookies();
        let tokens = cookies.get("token");
        match tokens {
            Some(token) => match verify_token(&token.value().to_string()) {
                Some(user_token) => Outcome::Success(user_token),
                None => Outcome::Failure((Status::Unauthorized, ())),
            },
            None => Outcome::Failure((Status::Unauthorized, ())),
        }
    }
}
